package gates;

import register.Register;
import gates.MatrixGate;
import mathtools.*;

/**Matrix representation of a Y pauli gate.
 * This implements Gate interface, so consult this for public methods.
 * The constructor for this gate is protected, so generating such an object is left
 * for the factory class <code>Factory</code>
 * @author Jan Zaucha
 * 
 */
public class YMatrix extends MatrixGate{

	/**target bit*/
	private int target;
	/**Y matrix*/
	private Matrix Y;

	
	/**
	 * Protected constructor for a matrix representation of a Y pauli gate
	 * Requires a target bit for the gate to operate on, however if the target bit is
	 * -1 the gate will be applied to all qubits
	 * @param target the target bit for phase shift (counting qubits from 0)
	 */
	protected YMatrix (int target){
		this.target=target;
		Y = new Matrix(2,2,0);
		Y.setElement(1,0,new Complex(0,-1));
		Y.setElement(0, 1,new Complex(0, 1) );
	}
	
	
	/**Applies the Y gate to register r.
	 * 
	 * @param r - Register to apply gate to.
	 */
	public void apply(Register r) {
		if (target >= r.getNumQbits() || target < -1){
			throw new IllegalArgumentException("Target qubit not in register. Register size (qubits): " + r.getNumQbits() + " Target qubit: " + target);
		}

		//phase shifting whole reg
		if (target==-1){
			gate = Y;
			for (int i=1;i<r.getNumQbits();i++){
				gate = gate.tensorProduct(Y);
			}
		}
		//case where target bit = 0
		else if (target==0){
			gate = Y;
			if (r.getNumQbits()>1){
				for (int i=1;i<r.getNumQbits();i++){
					gate = gate.tensorProduct(Matrix.IdentityMatrix(2));
				}
			}
		}
		//all other cases
		else{
			gate=Matrix.IdentityMatrix(2);
			for (int i=1; i<target; i++){
				gate = gate.tensorProduct(Matrix.IdentityMatrix(2));
			}
			gate=gate.tensorProduct(Y);
			for (int i=target+1;i<r.getNumQbits();i++){
				gate = gate.tensorProduct(Matrix.IdentityMatrix(2));
			}
		}
		
		//now apply gate to register
		Matrix r_updated = gate.multiplyMatrix(r);
		r.update(r_updated);
		//normalise reg
		r.normalise();
	}

	/**
	 * Returns the name of the gate regardless of representation.
	 */
	public String getName() {
		return "Y Pauli";
	}
	
}